﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Linq.Expressions;
using System.Data.Linq.Mapping;
using System.Collections.ObjectModel;
using System.Text;
using System.Data.Linq;
using System.Reflection;
using LinqExt;
using Hotnet.Entity;
using System.Data.Common;
using System.Data;

namespace Hotnet.Dal.BaseRepository
{
	public class Repository<T> : IRepository<T> where T : class, new()
	{
		// 保存pk pkTable的缓存字典
		private static Dictionary<Type, TableDef> s_TableDefCache = new Dictionary<Type, TableDef>();
		// 映射表的实例
		protected Table<T> givenTable;
		protected Table<T> diableCacheGiveTable;
		protected HotnetDBDataContext dataContext;
		protected HotnetDBDataContext disableCacheDataContext;
		protected string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["HotnetConnectionString"].ConnectionString;

		public Repository()
		{
			dataContext = DBDataContext.GetDataContext();
			disableCacheDataContext = new HotnetDBDataContext(connectionString);
			disableCacheDataContext.ObjectTrackingEnabled = false;
			diableCacheGiveTable = disableCacheDataContext.GetTable<T>();
			givenTable = dataContext.GetTable<T>();
		}

		/// <summary>
		/// 根据主键ID获得对应纪录实例
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public T Get(int id)
		{
			TableDef tableDef = GetTableDef<T>();
			var param = Expression.Parameter(typeof(T), "e");
			var predicate = Expression.Lambda<Func<T, Boolean>>(Expression.Equal(Expression.Property(param, tableDef.PKFieldName), Expression.Constant(id)), param);
			return diableCacheGiveTable.SingleOrDefault(predicate);
		}

		/// <summary>
		/// 根据主键ID删除对应纪录
		/// </summary>
		/// <param name="id"></param>
		public virtual void Delete(int id)
		{
			givenTable.DeleteByPK(id);
		}

		/// <summary>
		/// 根据实例删除数据库中记录
		/// </summary>
		/// <param name="target"></param>
		public virtual void Delete(T model)
		{
			givenTable.DeleteOnSubmit(model);
			dataContext.SubmitChanges();
		}

		/// <summary>
		/// 根据条件删除记录
		/// </summary>
		/// <param name="where"></param>
		public void DeleteBy(Expression<Func<T, Boolean>> where)
		{
			givenTable.DeleteBatch(where);
		}

		/// <summary>
		/// 列出表所有记录
		/// </summary>
		/// <returns></returns>
		public T[] List()
		{
			return diableCacheGiveTable.ToArray();
		}

		/// <summary>
		/// 列出表所有记录
		/// </summary>
		/// <returns></returns>
		public T[] List<TKey>(Expression<Func<T, TKey>> keySelector, bool Descending)
		{
			if (Descending)
			{
				return diableCacheGiveTable.OrderByDescending(keySelector).ToArray();
			}
			else
			{
				return diableCacheGiveTable.OrderBy(keySelector).ToArray();
			}
		}

		/// <summary>
		/// 根据where 语句 查询对应纪录
		/// </summary>
		/// <param name="where"></param>
		/// <returns></returns>
		public T[] ListBy(Expression<Func<T, Boolean>> where)
		{
			return diableCacheGiveTable.Where<T>(where).ToArray();
		}

		/// <summary>
		/// 根据where 语句 查询对应纪录
		/// </summary>
		/// <param name="where"></param>
		/// <returns></returns>
		public T[] ListBy<TKey>(Expression<Func<T, Boolean>> where, Expression<Func<T, TKey>> keySelector, bool Descending)
		{
			if (Descending)
			{
				return diableCacheGiveTable.Where<T>(where).OrderByDescending(keySelector).ToArray();
			}
			else
			{
				return diableCacheGiveTable.Where<T>(where).OrderBy(keySelector).ToArray();
			}
		}

		/// <summary>
		/// 根据where 语句 查询出一条记录
		/// </summary>
		/// <param name="where"></param>
		/// <returns></returns>
		public T GetBy(Expression<Func<T, bool>> where)
		{
			return diableCacheGiveTable.Where<T>(where).FirstOrDefault();
		}

		/// <summary>
		/// 插入一条记录
		/// </summary>
		/// <param name="model"></param>
		public void Insert(T model)
		{

			using (HotnetDBDataContext ctx = new HotnetDBDataContext(connectionString))
			{
				ctx.GetTable<T>().InsertOnSubmit(model);
				ctx.SubmitChanges();
			}
			//givenTable.InsertOnSubmit(model);
			//List<ChangedItems<T>> listChange = dataContext.GetChangedItems<T>();
			//dataContext.SubmitChanges();

		}

		/// <summary>
		/// 除了identity字段,其他字段全部更新
		/// </summary>
		/// <param name="model">要更新的实例</param>
		/// <param name="isUpdateNull">是否更新null字段</param>
		/// <param name="isUpdateZero">是否更新0字段</param>
		public void UpdateSelfDefine(T model, bool isUpdateNull,bool isUpdateZero)
		{
			Type type = typeof(T);

			TableDef tableDef = GetTableDef<T>();
			PropertyInfo keyProperty = type.GetProperty(tableDef.PKFieldName);
			ParameterExpression typeParameter = Expression.Parameter(type, "t");
			MemberExpression memberExpression = Expression.Property(typeParameter, tableDef.PKFieldName);
			ConstantExpression keyValue = Expression.Constant((int)keyProperty.GetValue(model, null));
			BinaryExpression comparison = Expression.Equal(memberExpression, keyValue);
			Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(comparison, typeParameter);
			IQueryable<T> originalQuery = givenTable.Where(lambda);

			MemberInitExpression memberInitExpression = CreateMemberInitExpression(model, isUpdateNull,isUpdateZero);
			Expression<Func<T, T>> expression = Expression<Func<T, T>>.Lambda<Func<T, T>>(memberInitExpression, typeParameter);
			givenTable.UpdateBatch(originalQuery, expression);
		}

		/// <summary>
		/// 是否更新null，值为0 更新
		/// </summary>
		/// <param name="model"></param>
		/// <param name="isUpdateNull"></param>
		private void UpdateSelfDefine(T model, bool isUpdateNull)
		{
			UpdateSelfDefine(model, isUpdateNull, true);
		}

		public void UpdateExcludeGenerateKey(T model)
		{
			UpdateSelfDefine(model, true);
		}

		/// <summary>
		/// 如果Model字段为null, "", 0 则不更新数据库中对应的字段。
		/// </summary>
		/// <param name="model"></param>
		public void Update(T model)
		{
			UpdateSelfDefine(model, false);
		}

		public void Update(int pkId, Expression<Func<T, T>> evaluator)
		{
			TableDef tableDef = GetTableDef<T>();
			ParameterExpression typeParameter = Expression.Parameter(typeof(T), "t");
			MemberExpression memberExpression = Expression.Property(typeParameter, tableDef.PKFieldName);
			ConstantExpression keyValue = Expression.Constant(pkId);
			BinaryExpression comparison = Expression.Equal(memberExpression, keyValue);
			givenTable.UpdateBatch(Expression.Lambda<Func<T, bool>>(comparison, typeParameter), evaluator);
		}

		public void Update(IQueryable<T> entities, Expression<Func<T, T>> evaluator)
		{
			givenTable.UpdateBatch(entities, evaluator);
		}

		public void Update(Expression<Func<T, bool>> filter, Expression<Func<T, T>> evaluator)
		{
			givenTable.UpdateBatch(filter, evaluator);
		}

		public static MemberInitExpression CreateMemberInitExpression(T model, bool isUpdateNull,bool isUpdateZero)
		{
			Type type = typeof(T);
			NewExpression newModel = Expression.New(type);

			List<MemberBinding> memberBindings = new List<MemberBinding>();
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
			
			foreach (var property in properties)
			{
				//object[] assAttr = property.GetCustomAttributes(typeof(AssociationAttribute), false);
				object[] colAttr = property.GetCustomAttributes(typeof(ColumnAttribute), false);
				if (colAttr.Length == 0)
					continue;
				ColumnAttribute columnAttr = (ColumnAttribute)colAttr[0];
				if (columnAttr.IsDbGenerated)
					continue;

				object obj = property.GetValue(model, null);

				if ((obj == null || string.IsNullOrEmpty(obj.ToString())) && !isUpdateNull)
					continue;

				if (obj.GetType() == typeof(DateTime) && ((DateTime)(obj)) == DateTime.MinValue)
					continue;

				if (obj.GetType() == typeof(int) && (int)obj == 0 && !isUpdateZero)
					continue;

				MemberBinding memberBinding = Expression.Bind(property, Expression.Constant(property.GetValue(model, null)));
				memberBindings.Add(memberBinding);
			}

			MemberInitExpression memberInitExpression = Expression.MemberInit(newModel, memberBindings);

			return memberInitExpression;
		}


		private static TableDef GetTableDef<TEntity>() where TEntity : class
		{
			Type entityType = typeof(TEntity);
			if (!s_TableDefCache.ContainsKey(entityType))
			{
				lock (s_TableDefCache)
				{
					if (!s_TableDefCache.ContainsKey(entityType))
					{
						object[] attributes = entityType.GetCustomAttributes(typeof(TableAttribute), true);
						string tableName = (attributes[0] as TableAttribute).Name;
						if (tableName.StartsWith("dbo."))
							tableName = tableName.Substring("dbo.".Length);
						string pkFieldName = "ID";


						// Find the property which is the primary key so that we can find the 
						// primary key field name in database
						foreach (PropertyInfo prop in entityType.GetProperties())
						{
							object[] columnAttributes = prop.GetCustomAttributes(typeof(ColumnAttribute), true);
							if (columnAttributes.Length > 0)
							{
								ColumnAttribute columnAtt = columnAttributes[0] as ColumnAttribute;
								if (columnAtt.IsPrimaryKey)
									pkFieldName = columnAtt.Storage.TrimStart('_');
							}
						}

						var tableDef = new TableDef { TableName = tableName, PKFieldName = pkFieldName };
						s_TableDefCache.Add(entityType, tableDef);
						return tableDef;
					}
					else
					{
						return s_TableDefCache[entityType];
					}
				}
			}
			else
			{
				return s_TableDefCache[entityType];
			}
		}
	}

	class TableDef
	{
		public string TableName;
		public string PKFieldName;
	}
}
